package gpstools.cmd;

import gpstools.calc.Calculator;
import gpstools.data.GpxExporter;
import gpstools.data.TcxExporter;
import gpstools.data.TrackLoader;
import gpstools.data.UnsupportedFormatException;
import gpstools.track.Lap;
import gpstools.track.Track;
import gpstools.track.Track.Builder;
import gpstools.track.TrackPoint;

import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;
import java.util.List;

import org.apache.commons.cli.BasicParser;
import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.CommandLineParser;
import org.apache.commons.cli.HelpFormatter;
import org.apache.commons.cli.Option;
import org.apache.commons.cli.OptionBuilder;
import org.apache.commons.cli.Options;
import org.apache.commons.cli.ParseException;

public class Main {
	@SuppressWarnings("static-access")
	public static void main(String[] args) throws IOException, UnsupportedFormatException {
		CommandLineParser parser = new BasicParser();

		Options options = new Options();
		Option splitOption = OptionBuilder
			.hasArgs()
			.withDescription("split the track by distance specifing cut points")
			.create("split");
		options.addOption(splitOption);
		Option lapOption = OptionBuilder
			.hasArgs(1)
			.withDescription("extract the selected lap to a separate file")
			.create("lap");
		options.addOption(lapOption);
		options.addOption("tcx", false, "saves as tcx file");
		options.addOption("gpx", false, "saves as gpx file");

		// options.addOption(new Option("e", true, "extract lap"));
		// options.addOption(new Option("o", true, "set output file name"));
		// options.addOption(new Option("tcx", false,
		// "use tcx format for output"));

		try {
			CommandLine line = parser.parse(options, args);
			String[] arguments = line.getArgs();

			if (args.length == 0) {
				HelpFormatter formatter = new HelpFormatter();
				formatter.setArgName("file");
				formatter.printHelp("java -jar gps-tools.jar file [options]", options);
				return;
			}

			String fileName = line.getArgs()[0];

			TrackLoader trackLoader = new TrackLoader(fileName);

			System.out.println("file: " + fileName);
			System.out.println();
			Track next;
			while ((next = trackLoader.next()) != null) {
				System.out.println("track: " + next.getName());
				if (line.hasOption("split")) {
					split(next, line.getOptionValues("split"));
				} else if (line.hasOption("lap")) {
					lap(next, line.getOptionValues("lap"));
				} else if (line.hasOption("tcx")) {
					saveAsTcx(next, fileName);
				} else if (line.hasOption("gpx")) {
					saveAsGpx(next, fileName);
				} else {
					printDetails(next);
				}
			}

		} catch (ParseException e) {
			System.out.println(e.getLocalizedMessage());
		}

	}

	private static void lap(Track track, String[] optionValues)
		throws IOException
	{
		int lapNumber = Integer.parseInt(optionValues[0]);
		int lapNumberZeroBased = lapNumber - 1;

		Lap lap = track.getLaps().get(lapNumberZeroBased);

		Builder builder = Track.builder().add(
			lap.getPoints().toArray(new TrackPoint[lap.getPoints().size()]));

		storeTrack(builder, 0);
	}

	private static void saveAsGpx(Track next, String fileName)
		throws FileNotFoundException
	{
		new GpxExporter().add(next).store(
			new FileOutputStream(fileName + ".gpx"));
	}

	private static void saveAsTcx(Track next, String fileName)
		throws FileNotFoundException
	{
		new TcxExporter().add(next).store(
			new FileOutputStream(fileName + ".tcx"));
	}

	private static void split(Track next, String[] optionValues)
		throws IOException
	{
		System.out.println("split" + Arrays.asList(optionValues));
		Builder builder = Track.builder();

		int index = 0;
		double distance = Double.parseDouble(optionValues[index]);

		Calculator calculator = new Calculator();
		List<TrackPoint> points = next.getPoints();
		for (TrackPoint point : points) {
			builder.add(point);

			if (index < optionValues.length) {
				calculator.add(point);

				if (calculator.getDistance() >= distance) {
					storeTrack(builder, index++);
					if (index < optionValues.length)
						distance = Double.parseDouble(optionValues[index]);
					builder = Track.builder().add(point);
				}
			}
		}
		if (!builder.isEmpty()) {
			storeTrack(builder, index);
		}
	}

	private static void storeTrack(Builder builder, int index)
		throws IOException
	{
		String name = String.format("%02d", index);
		Track track = builder.name(name).build();
		String fileName = "out" + name + ".gpx";
		new GpxExporter(track).store(fileName);
		System.out.println(fileName + " saved.");
	}

	private static void printDetails(Track track)
	{
		Date startDate = new Date(track.getPoints().get(0).getTime()
			.longValue());
		Calculator calculator = new Calculator();
		List<TrackPoint> points = track.getPoints();
		for (TrackPoint point : points) {
			calculator.add(point);
		}
		System.out.printf("started: %s\n", new SimpleDateFormat(
			"yyyy-MM-dd-HH-mm-ss").format(startDate));
		System.out.printf("distance: %.1f km\n",
			calculator.getDistance() / 1000);
		System.out.printf("ascent distance: %.1f km\n",
			calculator.getElevationGainDistance() / 1000);
		System.out.printf("total points: %d\n", track.getPoints().size());

		double elevationGain = calculator.getElevationGain();
		System.out.printf("elevation gain: %s\n", elevationGain);
		System.out.println();

		System.out.printf("laps: %d\n", track.getLaps().size());
		List<Lap> laps = track.getLaps();
		int i = 1;
		for (Lap lap : laps) {
			i = printLap(i, lap);
		}
	}

	private static int printLap(int i, Lap lap)
	{
		System.out.printf("lap %2d:", i++);
		System.out.printf(" %8.2f km",
			Calculator.distance(lap.getPoints()) / 1000);
		List<TrackPoint> points = lap.getPoints();
		Calculator calculator = new Calculator();
		for (TrackPoint trackPoint : points) {
			calculator.add(trackPoint);
		}

		System.out.printf(" %5.0fm", calculator.getElevationGain());
		System.out.printf(" %3dbpm", calculator.getAverageHeartRate());
		System.out.println();
		return i;
	}

}
